3. 쿠버네티스 개요2

3.4 애플리케이션 배포

3.4.1 디플로이먼트

디플로이먼트의 스케일링 기능:

디플로이먼트는 파드 그룹을 스케일링할 수 있음. 매니페스트 파드 개수를 변경하고 kubectl apply 명령어로 다시 선언하면 매니페스트에 따라 파드를 스케일링해서 실행함

용어 정리

  • 스케일링(Scaling): 애플리케이션 인스턴스 수나 자원을 조절하는 것
  • 스케일 아웃(Scale Out): 인스턴스 수를 늘려 처리 능력 확장 (수평 확장)
  • 스케일 인(Scale In): 인스턴스 수를 줄여 자원 절약 (수평 축소)
  • 디플로이먼트(Deployment): Pod의 배포, 스케일링, 업데이트를 관리하는 Kubernetes 리소스

스케일링 동작 방식:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[매니페스트 파일 수정]
    replicas: 1 → 3
    ↓
[kubectl apply 실행]
    ↓
[쿠버네티스 조정]
    │
    ├─ 현재 상태: Pod 1개
    ├─ 원하는 상태: Pod 3개
    └─ 차이 감지 → Pod 2개 추가 생성

셀프 힐링 (Self-Healing) 기능:

디플로이먼트 기능에는 셀프 힐링이 있음. 노드에 장애가 발생하거나 파드 동작 불량으로 클러스터 전체의 파드 가동 개수가 지정한 숫자보다 줄어들면 자동적으로 새로운 파드를 실행해서 복구를 시도하는 기능

셀프 힐링 동작 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

정상 상태
    [Node1: Pod1] [Node2: Pod2] [Node3: Pod3]
    설정된 replicas: 3개
    ↓
노드 장애 발생
    [Node1: Pod1] [Node2: ✗ 장애] [Node3: Pod3]
    실제 가동: 2개 (부족!)
    ↓
디플로이먼트 자동 복구
    [Node1: Pod1] [Node1: Pod2-new] [Node3: Pod3]
    다시 3개로 복구 완료

예를 들어 클러스터에 노드 고장이 발생했다면 고장 난 노드에서 작동하던 파드도 다운 상태가 됨. 클러스터 전체에서 가동된 파드 개수가 설정된 개수보다 적어지는데, 따라서 가동 중인 파드 개수가 다시 설정한 이상적인 개수를 유지하도록 디플로이먼트는 새로운 파드를 작성해서 클러스터에 일정한 개수의 파드가 언제나 가동 중인 상태를 유지하려고 함


스케일링 예제:

파일명: nginx-deployment.yaml을 복사 후 수정

apiVersion: apps/v1 # Kubernetes API 버전 (apps/v1은 Deployment, StatefulSet 등의 워크로드용)
kind: Deployment # 리소스 타입 - Deployment (파드 그룹 관리 및 스케일링)
metadata:
  name: nginx-deployment # 디플로이먼트 이름 (클러스터 내 고유 식별자)
spec:
  replicas: 3 # 가동할 파드 개수를 1에서 3으로 변경 (스케일 아웃)
  selector: # 어떤 파드를 관리할지 선택하는 조건
    matchLabels:
      app: nginx # "app=nginx" 레이블을 가진 파드를 이 디플로이먼트가 관리
  template: # 파드 생성 시 사용할 템플릿
    metadata:
      labels:
        app: nginx # 생성되는 파드에 부여할 레이블 (selector와 일치해야 함)
    spec: # 파드 스펙 정의
      containers: # 파드 내에서 실행할 컨테이너 목록
        - name: nginx # 컨테이너 이름
          image: nginx # Docker Hub의 nginx 이미지 (태그 미지정 시 latest)
          ports:
            - containerPort: 80 # 컨테이너가 노출하는 포트 (nginx 기본 HTTP 포트)

스케일링 적용:

# 변경된 매니페스트 적용
kubectl apply -f nginx-deployment-3.yaml

# 디플로이먼트와 파드 상태 확인
kubectl get deployment
kubectl get pods

셀프 힐링 테스트:

# 파드 하나를 수동으로 삭제
kubectl delete pod nginx-deployment-<실제-파드-이름>

# 파드 목록 확인
kubectl get pods

그러면 일시적으로 클러스터에 파드 총 개수가 3보다 적어짐. 매니페스트는 파드 3개가 가동된다고 설정했으므로 디플로이먼트는 새로운 nginx 파드를 자동으로 가동해서 클러스터를 이상적인 상태로 유지

다시 kubectl get pods를 실행하면 3개의 파드가 실행되는 것을 확인할 수 있음(AGE 가 다름)


롤링 업데이트 (Rolling Update):

디플로이먼트는 업데이트와 롤백할 때도 편리한 기능을 제공함. 단순한 업데이트는 현재 가동 중인 버전의 파드를 모두 삭제하고 새로운 버전의 파드를 일괄적으로 재배포하는 방식

쿠버네티스는 일정 개수의 파드가 클러스터에서 언제나 가동 중인 상태를 유지하면서 파드 그룹을 점진적으로 업데이트 또는 롤백하는 롤링 업데이트 방식도 지원. 이런 방식으로 서비스 중단 없이 디플로이먼트를 업데이트할 수 있음

용어 정리

  • 롤링 업데이트(Rolling Update): 서비스 중단 없이 점진적으로 Pod를 새 버전으로 교체하는 배포 방식
  • 다운타임(Downtime): 서비스가 중단되어 사용 불가능한 시간
  • 무중단 배포(Zero-Downtime Deployment): 서비스 중단 없이 새 버전을 배포하는 방식

롤링 업데이트 동작 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

초기 상태 (nginx:1.28)
    [Pod1: v1] [Pod2: v1] [Pod3: v1]
    사용 가능: 3개
    ↓
롤링 업데이트 시작
    [Pod1: v1] [Pod2: v1] [Pod3: v1] [Pod4: v2 생성]
    사용 가능: 3개 (다운타임 없음)
    ↓
    [Pod1: v1 삭제] [Pod2: v1] [Pod3: v1] [Pod4: v2]
    사용 가능: 3개
    ↓
    [Pod2: v1] [Pod3: v1] [Pod4: v2] [Pod5: v2 생성]
    사용 가능: 3개
    ↓
    [Pod2: v1 삭제] [Pod3: v1] [Pod4: v2] [Pod5: v2]
    사용 가능: 3개
    ↓
최종 상태 (nginx:1.28-alpine)
    [Pod3: v1 삭제] [Pod4: v2] [Pod5: v2] [Pod6: v2]
    사용 가능: 3개

업데이트 예제:

파일명: nginx-deployment-alpine.yaml

apiVersion: apps/v1 # Kubernetes API 버전
kind: Deployment # 리소스 타입 - Deployment
metadata:
  name: nginx-deployment # 디플로이먼트 이름 (기존과 동일해야 업데이트됨)
spec:
  replicas: 3 # 파드 개수 유지 (3개)
  selector: # 파드 선택 조건
    matchLabels:
      app: nginx # "app=nginx" 레이블을 가진 파드 관리
  template: # 파드 템플릿 (이 부분이 변경됨)
    metadata:
      labels:
        app: nginx # 생성되는 파드의 레이블
    spec:
      containers:
        - name: nginx # 컨테이너 이름
          image: nginx:1.28-alpine # 이미지를 nginx:1.28에서 nginx:1.28-alpine으로 변경 (Alpine Linux 기반 경량 이미지)
          ports:
            - containerPort: 80 # HTTP 포트

롤링 업데이트 실행:

파드를 업데이트하려면 이미지 이름을 변경한 매니페스트를 새로 kubectl apply로 선언

# 업데이트된 매니페스트 적용
kubectl apply -f nginx-deployment-alpine.yaml

# 롤링 업데이트 진행 상황 실시간 관찰 (-w는 watch 모드)
kubectl get deployments nginx-deployment -w

그러면 새로 선언된 매니페스트에 따라 쿠버네티스는 구체적인 파드 업데이트 작업을 시작함. 롤링 업데이트가 활성화되어 있으므로 파드가 점진적으로 교체되도록 신규 파드 작성과 기존 파드 삭제 작업을 교대로 실시

kubectl get deployments 명령어를 -w 옵션과 함께 실행하면 디플로이먼트의 파드 개수 변화를 관찰할 수 있음. AVAILABLE은 사용 가능 파드 개수를 의미하고 업데이트 중에도 언제나 일정 개수 이상의 파드가 사용 가능 상태임을 알 수 있음

업데이트 확인:

# 파드 목록 확인
kubectl get pods

# 디플로이먼트의 이미지 변경 여부 확인 (커스텀 컬럼 사용)
kubectl get pods -l=app=nginx -o custom-columns=NAME:.metadata.name,IMAGE:.spec.containers[0].image,STATUS:.status.phase

최종적으로 kubectl get pods로 파드 목록을 확인하면 이미지가 모두 새로운 nginx:1.28-alpine으로 교체됨

-o custom-columns 옵션은 매니페스트 파일에 선언된 내용을 커스텀 컬럼으로 출력하는 방법


3.4.2 스테이트풀셋

스테이트풀 애플리케이션:

파드와 컨테이너는 장기적인 상태를 유지하지 않는 스테이트리스 또는 일시적인 실행 단위. 파드는 영구적 데이터를 저장하지 않아서 파드가 종료되면 파일시스템에 기록된 내용도 삭제

용어 정리

  • 스테이트리스(Stateless): 상태를 저장하지 않는 애플리케이션. 요청 간에 데이터를 유지하지 않음
  • 스테이트풀(Stateful): 상태를 유지하는 애플리케이션. 데이터베이스처럼 영구적 데이터 저장 필요
  • 영속성(Persistence): 데이터가 프로세스 종료 후에도 유지되는 특성

스테이트리스 vs 스테이트풀:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[스테이트리스 (Stateless)]
    │
    ├─ 웹 서버 (nginx, Apache)
    ├─ API 서버
    └─ 로드 밸런서

    특징:
    ├─ 파드 종료 시 데이터 소멸
    ├─ 모든 파드가 동일
    └─ 어느 파드에 요청해도 결과 동일

vs

[스테이트풀 (Stateful)]
    │
    ├─ 데이터베이스 (MySQL, MongoDB)
    ├─ 메시지 큐 (Kafka)
    └─ 분산 스토리지 (Cassandra)

    특징:
    ├─ 파드마다 고유한 ID와 스토리지
    ├─ 재시작 후에도 데이터 유지
    └─ 파드 순서와 안정성 중요

하지만 서비스 특성상 영구적인 데이터가 없이 구현하기 어렵거나, 데이터베이스처럼 장기적인 상태를 저장하는 애플리케이션을 컨테이너로 구성해야 할 때도 있음. 쿠버네티스는 이런 사례에 스테이트풀 컨테이너의 실행도 지원

스테이트풀셋의 특징:

스테이트풀셋은 스테이트풀 파드 관리에 유용한 기능. 디플로이먼트는 관리하는 파드 그룹을 전부 동일하게 획일적으로 다루지만, 스테이트풀셋은 관리 대상 파드 그룹에 포함된 각 파드를 구별해서 다룸

디플로이먼트 vs 스테이트풀셋:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Deployment]
    nginx-deployment-a1b2c3
    nginx-deployment-d4e5f6  ← 랜덤한 이름
    nginx-deployment-g7h8i9

    특징:
    ├─ 파드 이름이 무작위
    ├─ 생성/삭제 순서 무관
    └─ 파드를 구별할 필요 없음

vs

[StatefulSet]
    nginx-statefulset-0
    nginx-statefulset-1      ← 순차적 인덱스
    nginx-statefulset-2

    특징:
    ├─ 파드 이름에 고유 인덱스 (0, 1, 2...)
    ├─ 순서대로 생성/삭제
    └─ 각 파드에 고유한 볼륨 할당

각 파드에 고유한 인덱스와 호스트명을 부여. 헤드리스 서비스 기능과 조합하면 쿠버네티스 클러스터 내부 DNS에서 각 파드에 대해 아래와 같은 형식의 이름을 얻을 수 있어서, 이 주소를 통해 각 파드를 독립적으로 다루거나 또는 협력적으로 다룰 수 있음

파드 DNS 이름 형식:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

$(스테이트풀셋명)-$(인덱스).$(서비스명).$(네임스페이스).svc.cluster.local

예시:
nginx-statefulset-0.nginx.default.svc.cluster.local
nginx-statefulset-1.nginx.default.svc.cluster.local
nginx-statefulset-2.nginx.default.svc.cluster.local

활용:
├─ 각 파드에 직접 접근 가능
├─ 마스터-슬레이브 구조 구현
└─ 파드 간 협력 작업 가능
용어 정리

  • 인덱스(Index): StatefulSet에서 각 Pod에 부여되는 고유 번호 (0, 1, 2, ...)
  • 마스터-슬레이브(Master-Slave): 하나의 주 노드(마스터)가 데이터를 관리하고 복제 노드(슬레이브)가 복제하는 구조

헤드리스 서비스 (Headless Service):

일반 서비스는 단일 ClusterIP를 할당받아 트래픽을 파드들에게 로드 밸런싱하지만, 헤드리스 서비스는 clusterIP: None으로 설정하여 ClusterIP를 할당받지 않음

일반 서비스 vs 헤드리스 서비스:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[일반 Service]
    ClusterIP: 10.96.100.50
    ↓ (로드 밸런싱)
    ├─ Pod1: 10.244.1.10
    ├─ Pod2: 10.244.1.11
    └─ Pod3: 10.244.1.12

    특징: IP 주소로 접근, 무작위로 파드 선택

vs

[Headless Service]
    ClusterIP: None
    ↓ (DNS 레코드만 제공)
    ├─ pod-0.service.ns.svc.cluster.local → 10.244.1.10
    ├─ pod-1.service.ns.svc.cluster.local → 10.244.1.11
    └─ pod-2.service.ns.svc.cluster.local → 10.244.1.12

    특징: DNS 이름으로 특정 파드에 직접 접근 가능

헤드리스 서비스를 사용하면:

용어 정리

  • 헤드리스 서비스(Headless Service): ClusterIP가 없는 서비스. 각 Pod에 개별 DNS 레코드를 제공
  • ClusterIP: 클러스터 내부에서만 접근 가능한 가상 IP 주소. 일반 서비스의 기본 타입
  • 로드 밸런싱(Load Balancing): 트래픽을 여러 서버로 분산하여 부하를 균등하게 나누는 기술

스테이트풀셋의 스케일링:

스케일링을 통한 파드 작성이나 삭제는 인덱스 순서대로 이루어짐

스테이트풀셋 스케일링 순서:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[스케일 아웃: replicas 1 → 3]

    초기: pod-0
    ↓
    생성: pod-0, pod-1 (인덱스 0 → 1 순서)
    ↓
    최종: pod-0, pod-1, pod-2 (인덱스 0 → 1 → 2 순서)

[스케일 인: replicas 3 → 1]

    초기: pod-0, pod-1, pod-2
    ↓
    삭제: pod-0, pod-1 (인덱스 2 → 1 역순)
    ↓
    최종: pod-0 (인덱스가 큰 것부터 삭제)

예를 들어 인덱스 0번 파드로 다른 인덱스의 파드가 의존하는 애플리케이션(메인 데이터베이스 등)이 작동한다면 반드시 가장 먼저 가동되고 마지막에 삭제되도록 관리. 스테이트풀셋은 롤링 업데이트도 지원해서 인덱스가 큰 순서부터 업데이트

영구 볼륨 할당:

각 파드에는 고유의 볼륨을 할당할 수 있음

스테이트풀셋 볼륨 영속성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

초기 실행
    pod-0 → pvc-0 → pv-0 (볼륨 A)
    pod-1 → pvc-1 → pv-1 (볼륨 B)
    pod-2 → pvc-2 → pv-2 (볼륨 C)
    ↓
스케일 인 (replicas 3 → 1)
    pod-0 → pvc-0 → pv-0 (볼륨 A)
    (pod-1, pod-2 삭제되지만 볼륨 B, C는 유지)
    ↓
다시 스케일 아웃 (replicas 1 → 3)
    pod-0 → pvc-0 → pv-0 (볼륨 A, 기존 데이터)
    pod-1 → pvc-1 → pv-1 (볼륨 B, 이전 데이터 복원!)
    pod-2 → pvc-2 → pv-2 (볼륨 C, 이전 데이터 복원!)

예시: 인덱스 1 파드에는 대응하는 볼륨이 할당. 해당 파드를 종료하고 다시 같은 인덱스로 가동하면 전에 실행했을 때 할당된 볼륨을 이전과 똑같은 데이터를 지정한 상태로 다시 사용할 수 있음. 따라서 파드는 종료 전과 재실행 후에 일관된 인덱스와 데이터를 이어받은 형태로 장기적인 동작을 유지할 수 있음

용어 정리

  • PVC(PersistentVolumeClaim): 사용자가 스토리지를 요청하는 리소스. Pod와 PV 사이를 연결
  • PV(PersistentVolume): 클러스터 관리자가 프로비저닝한 실제 스토리지
  • volumeClaimTemplates: StatefulSet에서 각 Pod마다 자동으로 PVC를 생성하는 템플릿
  • ReadWriteOnce(RWO): 단일 노드에서만 읽기/쓰기가 가능한 볼륨 접근 모드


스테이트풀셋 예제:

파일명: nginx-statefulset.yaml

#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 스테이트풀셋 정의
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

apiVersion: apps/v1 # Kubernetes API 버전
kind: StatefulSet # 리소스 타입 - StatefulSet (상태를 유지하는 파드 그룹 관리)
metadata:
  name: nginx-statefulset # 스테이트풀셋 이름 (파드명 접두사로도 사용됨)
spec:
  selector: # 어떤 파드를 관리할지 선택하는 조건
    matchLabels:
      app: nginx # "app=nginx" 레이블을 가진 파드 관리
  serviceName: "nginx" # 헤드리스 서비스 이름 (파드 DNS 이름 생성에 사용)
  replicas: 3 # 파드 3개 실행 (nginx-statefulset-0, -1, -2 순서로 생성)
  template: # 파드 생성 시 사용할 템플릿
    metadata:
      labels:
        app: nginx # 생성되는 파드에 부여할 레이블
    spec:
      containers: # 파드 내에서 실행할 컨테이너 목록
        # nginx 이미지를 실행하는 컨테이너
        - name: nginx # 컨테이너 이름
          image: nginx:1.28 # Docker 이미지 (1.28 stable 버전 고정)
          ports:
            - containerPort: 80 # nginx HTTP 포트
          volumeMounts: # 컨테이너에 마운트할 볼륨
            - name: vol # 아래에서 정의한 볼륨 이름 (volumeClaimTemplates의 name과 일치)
              mountPath: /usr/share/nginx/html # 컨테이너 내부 마운트 경로 (nginx 웹 루트 디렉터리)

        # 타임스탬프를 기록하는 컨테이너
        - name: recorder # 컨테이너 이름
          image: alpine:3.18 # 경량 리눅스 이미지 (Alpine Linux)
          command: ["sh"] # 실행할 명령어 (셸 스크립트)
          args: # 명령어에 전달할 인자
            - -euc # 옵션: -e (에러 시 종료), -u (미정의 변수 에러), -c (문자열을 명령어로 실행)
            - | # YAML 멀티라인 문자열 (파이프 기호)
              for i in $(seq 1 10) ; do                              # 1부터 10까지 반복
                echo '{"host": "'$(hostname)'", "time": "'$(date)'"}' >> /mnt/state.json  # JSON 형식으로 호스트명과 시간을 파일에 추가
                sleep 3                                              # 3초 대기
              done ; sleep infinity                                  # 10번 실행 후 무한 대기 (컨테이너 유지)
          volumeMounts: # 컨테이너에 마운트할 볼륨
            - name: vol # nginx 컨테이너와 동일한 볼륨 공유
              mountPath: /mnt/ # 컨테이너 내부 마운트 경로 (recorder는 여기에 파일 작성)

  #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  # 컨테이너끼리 공유하는 볼륨 정의 (볼륨 요청)
  #━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  volumeClaimTemplates: # 각 파드마다 자동으로 PVC를 생성하는 템플릿
    - metadata:
        name: vol # PVC 이름 (각 파드에 vol-nginx-statefulset-0, vol-nginx-statefulset-1 등으로 생성)
      spec:
        accessModes: # 볼륨 접근 모드
          - ReadWriteOnce # RWO: 단일 노드에서 읽기/쓰기 가능 (하나의 파드만 마운트 가능)
        resources: # 볼륨 리소스 요청
          requests:
            storage: 1Gi # 요청 스토리지 용량 (1기가바이트)

---
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 헤드리스 서비스 정의
#━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

apiVersion: v1 # Kubernetes API 버전 (v1은 Service, Pod 등 코어 리소스용)
kind: Service # 리소스 타입 - Service (파드 간 네트워크 통신 제공)
metadata:
  name: nginx # 서비스 이름 (스테이트풀셋의 serviceName과 일치해야 함)
  labels:
    app: nginx # 서비스 자체의 레이블
spec:
  ports:
    - port: 80 # 서비스가 노출하는 포트
  clusterIP:
    None # None으로 설정하면 헤드리스 서비스 (ClusterIP 할당 안 함)
    # 각 파드에 직접 접근 가능한 DNS 레코드 생성
  selector: # 트래픽을 전달할 파드 선택 조건
    app: nginx # "app=nginx" 레이블을 가진 파드를 서비스 대상으로 지정

파드가 인덱스 0부터 순서대로 하나씩 가동됨. kind: Service라고 표시된 부분은 헤드리스 서비스를 정의하고, 스테이트풀셋의 각 파드에 IP 주소가 아니라 파드명을 사용해서 통신할 수 있도록 함

스테이트풀셋 배포:

# 스테이트풀셋과 헤드리스 서비스 배포
kubectl apply -f nginx-statefulset.yaml

# 파드 생성 확인 (0번부터 순서대로 생성됨)
kubectl get pods -l=app=nginx -w

이번에 배포한 파드는 nginx 외에도 recorder 컨테이너 1개가 실행. 이 컨테이너는 가동 직후부터 타임스탬프를 일정 간격으로 출력. 이 컨테이너는 vol 볼륨을 공유

볼륨 공유 동작 방식:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Pod: nginx-statefulset-0]
    │
    ├─ [Container: recorder]
    │   └─ /mnt/state.json 에 타임스탬프 기록
    │       ↓ (볼륨 vol 공유)
    └─ [Container: nginx]
        └─ /usr/share/nginx/html/state.json 으로 읽기
            ↓
        http://nginx-statefulset-0.nginx:80/state.json 으로 제공

recorder는 타임스탬프를 볼륨에 기록. 기록한 데이터가 볼륨을 통해서 nginx 컨테이너와 공유되고 nginx 서버는 그 내용을 80번 포트로 공개

파드별 접근 테스트:

실제로 스테이트풀셋 파드 그룹을 가동한 후에 kubectl run으로 클러스터에서 일시적인 컨테이너를 실행해서 스테이트풀셋의 각 nginx 컨테이너에 접속해보면 recorder 컨테이너에서 기록된 타임스탬프를 확인할 수 있음

# 임시 파드를 생성하여 스테이트풀셋의 파드에 HTTP 요청
# --rm: 명령 실행 후 파드 자동 삭제
# -it: 인터랙티브 터미널 모드
# --restart=Never: 파드를 한 번만 실행 (재시작 안 함)
# sh -c: 셸 명령어 실행
# apk add curl: Alpine Linux에 curl 설치
# nginx-statefulset-0.nginx: 헤드리스 서비스를 통한 특정 파드 접근 (파드명.서비스명)
# head -3: 결과의 첫 3줄만 출력

kubectl run --rm -it svc-client --image=alpine:3.18 --restart=Never -- sh -c "apk add --no-cache curl && curl -s nginx-statefulset-0.nginx/state.json | head -3"

영속성 테스트:

실제로 파드 몇 개를 재시작하고, 파드 재시작 전후에 상태가 유지되는지 확인

영속성 테스트 시나리오:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1단계: 스케일 인 (3 → 1)
    pod-0, pod-1, pod-2
    ↓ (pod-2, pod-1 삭제)
    pod-0 (볼륨 유지됨)

2단계: 스케일 아웃 (1 → 3)
    pod-0
    ↓ (pod-1, pod-2 재생성)
    pod-0, pod-1, pod-2

    확인: 각 파드가 이전 볼륨을 재사용

먼저 kubectl scale 명령어로 스테이트풀셋을 1개로 스케일 인. 그러면 파드는 인덱스가 큰 순서부터 삭제되고 최종적으로 인덱스 0만 남음

# 스테이트풀셋 스케일 인 (3 → 1)
kubectl scale statefulsets nginx-statefulset --replicas=1

# 파드 상태 확인 (0번만 남음)
kubectl get pods -l=app=nginx

그리고 파드 개수를 다시 3개로 스케일 아웃. 즉, 2개의 파드가 순서대로 재작성됨. 이때 디플로이먼트와 다른 점은 각 인덱스의 파드는 종료 전에 사용한 볼륨이 다시 쿠버네티스에 의해서 할당된다는 점. 따라서 각 파드는 재시작 후에도 이전 상태를 이어받을 수 있고 재시작해도 자신의 상태를 잃지 않음

# 스테이트풀셋 스케일 아웃 (1 → 3)
kubectl scale statefulsets nginx-statefulset --replicas=3

# 파드 상태 확인
kubectl get pods -l=app=nginx

# 타임스탬프 재확인 (이전 데이터가 유지되는지 확인)
kubectl run --rm -it svc-client --image=alpine:3.18 --restart=Never -- sh -c "apk add --no-cache curl && curl -s nginx-statefulset-0.nginx/state.json | head -3"

리소스 정리:

# 스테이트풀셋과 서비스 삭제
kubectl delete -f nginx-statefulset.yaml

# PVC도 함께 삭제 (볼륨은 자동으로 삭제되지 않음)
kubectl delete pvc -l app=nginx

# PVC 삭제 확인
kubectl get pvc

이렇게 스테이트풀셋을 사용하면 파드 자체의 생존 기간을 넘어선 데이터와 상태를 관리할 수 있음

스테이트풀셋 핵심 정리:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

고유한 파드 ID
    ├─ pod-0, pod-1, pod-2 (순차적 인덱스)
    └─ pod-0.service.ns.svc.cluster.local (고유 DNS)

순서 보장
    ├─ 생성: 0 → 1 → 2 순서
    ├─ 삭제: 2 → 1 → 0 역순
    └─ 업데이트: 2 → 1 → 0 역순

영구 스토리지
    ├─ 각 파드에 고유한 PVC 자동 할당
    ├─ 파드 재시작 후에도 동일 볼륨 재사용
    └─ 데이터 영속성 보장

헤드리스 서비스
    ├─ clusterIP: None
    ├─ 각 파드에 직접 접근 가능
    └─ DNS 기반 파드 검색

참고 자료

공식 문서:

관련 주제: